package com.example.nacos.service.IndicatorCal;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.read.listener.PageReadListener;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.math3.stat.StatUtils;
import org.apache.commons.math3.util.FastMath;

import java.time.LocalDate;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.stream.Collectors;

@Slf4j
public class ProductMetricsCalculator {

    private static final double RISK_FREE_RATE = 0.03;
    private static final int TRADING_DAYS_PER_YEAR = 252;

    /**
     * 计算单个产品在特定周期的各项指标
     */
    public static MetricsResult calculateMetrics(List<ProductData> dataList, String portId, String periodType) {
        // 筛选指定产品数据
        List<ProductData> productData = dataList.stream()
                .filter(d -> d.getPortId().equals(portId))
                .sorted(Comparator.comparing(ProductData::getValidDate))
                .collect(Collectors.toList());

        if (productData.isEmpty()) {
            return null;
        }

        // 根据周期类型确定时间范围
        LocalDate endDate = productData.stream()
                .map(ProductData::getValidDate)
                .max(LocalDate::compareTo)
                .orElse(null);

        LocalDate startDate = calculateStartDate(productData, endDate, periodType);
        if (startDate == null) {
            return null;
        }

        // 筛选周期内数据
        List<ProductData> periodData = productData.stream()
                .filter(d -> !d.getValidDate().isBefore(startDate) && !d.getValidDate().isAfter(endDate))
                .collect(Collectors.toList());

        if (periodData.isEmpty()) {
            return null;
        }

        // 区分产品类型
        boolean hasNav = periodData.stream().anyMatch(d -> d.getYzNav() != null);
        boolean has7d = periodData.stream().anyMatch(d -> d.getQrnhsy() != null);

        MetricsResult result = null;

        if (hasNav) {
            result = calculateFundMetrics(periodData, portId, periodType);
            if (result == null) {
                log.warn("产品 " + portId + " 没有净值数据");
                return null;
            }
            result.addMetric(periodType, "产品类型", 1.0); // 基金产品标记
            result.getMetrics().put("productType", 1.0);
        } else if (has7d) {
            result = calculateMoneyFundMetrics(periodData, portId, periodType, startDate, endDate);
            result.addMetric(periodType, "产品类型", 2.0); // 货币型产品标记
            result.getMetrics().put("productType", 2.0);
        }

        return result;
    }

    /**
     * 计算基金产品指标
     */
    private static MetricsResult calculateFundMetrics(List<ProductData> periodData, String portId, String periodType) {
        MetricsResult result = new MetricsResult(portId, "基金产品");

        // 计算日收益率
        List<Double> dailyReturns = new ArrayList<>();
        for (int i = 1; i < periodData.size(); i++) {
            Double currentNav = periodData.get(i).getYzNav();
            Double previousNav = periodData.get(i-1).getYzNav();

            if (currentNav != null && previousNav != null && previousNav != 0) {
                double dailyReturn = (currentNav - previousNav) / previousNav;
                dailyReturns.add(dailyReturn);
            }
        }

        if (dailyReturns.isEmpty()) {
            return null;
        }

        double[] returnsArray = dailyReturns.stream().mapToDouble(Double::doubleValue).toArray();

        // 累计收益率
        double cumulativeReturn = 1.0;
        for (double ret : returnsArray) {
            cumulativeReturn *= (1 + ret);
        }
        cumulativeReturn -= 1;

        // 年化收益率
        double annualReturn = FastMath.pow(1 + cumulativeReturn, (double) TRADING_DAYS_PER_YEAR / returnsArray.length) - 1;

        // 最大回撤
        List<Double> navValues = periodData.stream()
                .map(ProductData::getYzNav)
                .filter(Objects::nonNull)
                .collect(Collectors.toList());

        double maxDrawdown = calculateMaxDrawdown(navValues);

        // 波动率（年化标准差）
        double volatility = FastMath.sqrt(StatUtils.variance(returnsArray)) * FastMath.sqrt(TRADING_DAYS_PER_YEAR);

        // 夏普比率
        double sharpeRatio = (annualReturn - RISK_FREE_RATE) / (volatility != 0 ? volatility : 1);

        result.addMetric(periodType, "年化收益率", annualReturn);
        result.addMetric(periodType, "累计收益率", cumulativeReturn);
        result.addMetric(periodType, "最大回撤", maxDrawdown);
        result.addMetric(periodType, "波动率", volatility);
        result.addMetric(periodType, "夏普比率", sharpeRatio);

        return result;
    }

    /**
     * 计算货币型产品指标
     */
    private static MetricsResult calculateMoneyFundMetrics(List<ProductData> periodData, String portId,
                                                           String periodType, LocalDate startDate, LocalDate endDate) {
        MetricsResult result = new MetricsResult(portId, "货币型产品");

        // 计算平均七日年化收益率
        double avg7dAnnual = periodData.stream()
                .map(ProductData::getQrnhsy)
                .filter(Objects::nonNull)
                .mapToDouble(Double::doubleValue)
                .average()
                .orElse(0);

        // 累计收益率（近似）
        long days = ChronoUnit.DAYS.between(startDate, endDate);
        double cumulativeReturn = avg7dAnnual * days / 365;

        result.addMetric(periodType, "年化收益率", avg7dAnnual);
        result.addMetric(periodType, "累计收益率", cumulativeReturn);
        result.addMetric(periodType, "最大回撤", 0.0);
        result.addMetric(periodType, "波动率", 0.0);
        result.addMetric(periodType, "夏普比率", Double.POSITIVE_INFINITY);

        return result;
    }

    /**
     * 计算最大回撤
     */
    private static double calculateMaxDrawdown(List<Double> navValues) {
        double maxDrawdown = 0.0;
        double peak = navValues.get(0);

        for (double nav : navValues) {
            if (nav > peak) {
                peak = nav;
            }
            double drawdown = (nav - peak) / peak;
            if (drawdown < maxDrawdown) {
                maxDrawdown = drawdown;
            }
        }

        return maxDrawdown;
    }

    /**
     * 根据周期类型计算开始日期
     */
    private static LocalDate calculateStartDate(List<ProductData> productData, LocalDate endDate, String periodType) {
        switch (periodType) {
            case "近一月":
                return endDate.minusDays(30);
            case "近三月":
                return endDate.minusDays(90);
            case "近六月":
                return endDate.minusDays(180);
            case "近一年":
                return endDate.minusDays(365);
            case "今年以来":
                return LocalDate.of(endDate.getYear(), 1, 1);
            case "成立以来":
                return productData.stream()
                        .map(ProductData::getValidDate)
                        .min(LocalDate::compareTo)
                        .orElse(null);
            default:
                return null;
        }
    }

    /**
     * 计算所有产品的所有周期指标
     */
    public static List<MetricsResult> calculateAllProducts(List<ProductData> dataList) {
        // 数据验证
        if (dataList.isEmpty()) {
            throw new IllegalArgumentException("数据列表为空");
        }

        // 获取所有唯一的产品ID
        Set<String> allPortIds = dataList.stream()
                .map(ProductData::getPortId)
                .collect(Collectors.toSet());

        // 定义需要计算的周期类型
        String[] periods = {"近一月", "近三月", "近六月", "近一年", "今年以来", "成立以来"};

        List<MetricsResult> results = new ArrayList<>();

        // 遍历所有产品
        for (String portId : allPortIds) {
            MetricsResult productResult = new MetricsResult(portId, "");

            // 确定产品类型
            List<ProductData> portData = dataList.stream()
                    .filter(d -> d.getPortId().equals(portId))
                    .toList();

            boolean hasNav = portData.stream().anyMatch(d -> d.getYzNav() != null);
            boolean has7d = portData.stream().anyMatch(d -> d.getQrnhsy() != null);

            String productType = hasNav ? "基金产品" : has7d ? "货币型产品" : "未知类型";
            productResult.setProductType(productType);

            // 计算每个周期的指标
            for (String period : periods) {
                MetricsResult periodMetrics = calculateMetrics(dataList, portId, period);
                if (periodMetrics != null) {
                    // 合并当前周期的指标
                    periodMetrics.getMetrics().forEach((key, value) -> {
                        if (!key.equals("productType")) {
                            productResult.addMetric(period, key.replace(period, ""), value);
                        }
                    });
                }
            }

            results.add(productResult);
        }

        return results;
    }

    /**
     * 主方法 - 示例用法
     */
    public static void main(String[] args) {
        // 创建示例数据
        String fileName =  "D:\\python\\ind_cal\\data\\TBPRDDAILY_1.xlsx";
        // 这里 需要指定读用哪个class去读，然后读取第一个sheet 文件流会自动关闭
        // 这里默认每次会读取100条数据 然后返回过来 直接调用使用数据就行
        // 具体需要返回多少行可以在`PageReadListener`的构造函数设置
        List<ProductData> sampleData = new ArrayList<>();

        EasyExcel.read(fileName, ProductData.class, new PageReadListener<ProductData>(sampleData::addAll)).sheet().doRead();
        if(sampleData.isEmpty()){
            System.out.println("没有数据");
            return;
        }
        log.info("------>{}", JSON.toJSONString(sampleData.get(0)));

        // 计算所有产品指标
        List<MetricsResult> results = calculateAllProducts(sampleData);

        // 输出结果
        System.out.println("金融产品指标计算结果:");
        System.out.println("=" + "=".repeat(80));

        for (MetricsResult result : results) {
            System.out.printf("产品ID: %s, 类型: %s%n", result.getProductId(), result.getProductType());

            result.getMetrics().forEach((metric, value) -> {
                if (!metric.equals("productType")) {
                    System.out.printf("  %s: %.4f%n", metric, value);
                }
            });
            System.out.println("-".repeat(80));
        }
    }

}
